<?php

// ------------------------------------------------------------------------
// |@Author       : Jarmin <topextend@qq.com>
// |@----------------------------------------------------------------------
// |@Date         : 2025-05-05 01:59:13
// |@----------------------------------------------------------------------
// |@LastEditTime : 2025-05-10 14:05:02
// |@----------------------------------------------------------------------
// |@LastEditors  : Jarmin <topextend@qq.com>
// |@----------------------------------------------------------------------
// |@Description  :
// |@----------------------------------------------------------------------
// |@FilePath     : ExceptionHandler.php
// |@----------------------------------------------------------------------
// |@Copyright (c) 2025 http://www.ladmin.cn All Rights Reserved.
// ------------------------------------------------------------------------
declare (strict_types=1);

namespace think\admin\exception;

use Throwable;
use think\Request;
use think\Response;
use think\admin\Exception;
use think\exception\Handle;
use think\exception\HttpException;
use think\admin\traits\ResponseTrait;
use think\admin\traits\ErrorCodeTrait;
use think\exception\ValidateException;
use think\admin\traits\HttpStatusTrait;
use think\admin\exception\AuthException;
use think\admin\exception\DataException;
use think\admin\exception\BusinessException;

/**
 * 异常处理器
 * Class ExceptionHandler
 * @package think\admin
 */
class ExceptionHandler extends Handle
{
    use ErrorCodeTrait;
    use HttpStatusTrait;
    use ResponseTrait;

    /**
     * 异常处理
     * @param Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render($request, Throwable $e): Response
    {
        // 如果是调试模式且有输出缓冲，则保留原始输出
        if ($this->app->isDebug() && ob_get_length() > 0) {
            $output = ob_get_clean();
            if ($output) {
                echo $output;
            }
        }

        // 默认错误码和信息
        $code = $e->getCode() ?: self::SYSTEM_ERROR;
        $message = $this->app->isDebug() ? $e->getMessage() : $this->getErrorMessage(self::SYSTEM_ERROR);
        $data = null;
        $httpCode = $this->getHttpStatusCode($code);

        // 处理不同类型的异常
        if ($e instanceof ValidateException) {
            // 验证异常
            $code = self::VALIDATE_ERROR;
            $message = $e->getMessage();
            $httpCode = $this->getHttpStatusCode($code);
        } elseif ($e instanceof AuthException) {
            // 认证异常
            $code = $e->getCode() ?: self::AUTH_ERROR;
            $message = $e->getMessage() ?: $this->getErrorMessage($code);
            $data = $e->getErrorData();
            $httpCode = $this->getHttpStatusCode($code);
        } elseif ($e instanceof BusinessException) {
            // 业务异常
            $code = $e->getCode() ?: self::BUSINESS_ERROR;
            $message = $e->getMessage() ?: $this->getErrorMessage($code);
            $data = $e->getErrorData();
            $httpCode = $this->getHttpStatusCode($code);
        } elseif ($e instanceof DataException) {
            // 数据异常
            $code = $e->getCode() ?: self::DATA_ERROR;
            $message = $e->getMessage() ?: $this->getErrorMessage($code);
            $data = $e->getErrorData();
            $httpCode = $this->getHttpStatusCode($code);
        } elseif ($e instanceof Exception) {
            // 其他自定义异常
            $code = $e->getCode();
            $message = $e->getMessage();
            $data = $e->getErrorData();
            $httpCode = $e->getHttpCode();
        } elseif ($e instanceof HttpException) {
            // HTTP异常
            $code = $e->getStatusCode();
            $message = $e->getMessage();
            $httpCode = $code;
        }

        // 如果是调试模式，添加调试信息
        if ($this->app->isDebug()) {
            $data = array_merge((array)$data, [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => explode("\n", $e->getTraceAsString()),
                'previous' => $e->getPrevious() ? [
                    'message' => $e->getPrevious()->getMessage(),
                    'code' => $e->getPrevious()->getCode(),
                ] : null,
            ]);
        }

        // 记录日志
        $this->reportException($e);

        // 返回 JSON 响应
        return $this->response($data, $message, $code, $httpCode);
    }

    /**
     * 记录异常日志
     * @param Throwable $e
     * @return void
     */
    protected function reportException(Throwable $e): void
    {
        // 不记录验证异常
        if ($e instanceof ValidateException) {
            return;
        }

        $logData = [
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'code' => $e->getCode(),
            'message' => $e->getMessage(),
            'trace' => $e->getTraceAsString(),
        ];

        // 添加请求信息
        if ($this->app->request) {
            $logData['request'] = [
                'url' => $this->app->request->url(true),
                'method' => $this->app->request->method(),
                'ip' => $this->app->request->ip(),
                'param' => $this->app->request->param(),
            ];
        }

        // 添加自定义异常数据
        if ($e instanceof Exception) {
            $logData['error_data'] = $e->getErrorData();
        }

        // 记录日志
        $this->app->log->error(
            sprintf('[%s] %s', get_class($e), $e->getMessage()),
            $logData
        );
    }
}
